#include "app/app_base.hpp"
#include <vector>
#include <thread>
#include <scripting/scripting_engine.h>

#include "data/version.hpp"
#include "nfd.h"

static ApplicationBase* gs_inst = nullptr;

struct AppBaseData
{
    using ExecutorType = asio::io_context::executor_type;
    using WorkGuard = asio::executor_work_guard<ExecutorType>;

    Version  app_version;
    EventBus bus;
    asio::io_context main_io;
    asio::io_context task_io;
    WorkGuard task_work_guard;
    std::vector<std::thread> task_threads;

    AppBaseData();
    ~AppBaseData();
};

EventBus ApplicationBase::Bus() const
{
    return d->bus;
}

ApplicationBase* ApplicationBase::Instance()
{
    assert(gs_inst != nullptr);
    return gs_inst;
}

ApplicationBase::ApplicationBase(Version app_version) : d{ new AppBaseData }
{
    NFD_Init();
    ScriptEngineInitialize();

    assert(gs_inst == nullptr);
    d->bus = std::make_shared<dexode::EventBus>();
    d->app_version = app_version;
    gs_inst = this;
}

ApplicationBase::~ApplicationBase()
{
    d->task_io.stop();

    delete d;
    gs_inst = nullptr;

    ScriptEngineShutdown();
    NFD_Quit();
}

asio::io_context& ApplicationBase::GetMainIO() const
{
    d->main_io.restart();
    return d->main_io;
}

asio::io_context& ApplicationBase::GetTaskIO() const
{
    return d->task_io;
}

AppBaseData::AppBaseData() :
    main_io(1),
    task_io(std::max<u32>(std::thread::hardware_concurrency() / 2, 2)),
    task_work_guard(make_work_guard(task_io))
{
    const auto cnt = std::max<u32>(std::thread::hardware_concurrency() / 2, 2);
    task_threads.reserve(cnt);
    for (auto i = 0u; i < cnt; ++i)
    {
        task_threads.emplace_back(
            [this]()->void
            {
                task_io.run();
            }
        );
    }
}

AppBaseData::~AppBaseData()
{
    main_io.stop();
    task_io.stop();

    for (auto& x : task_threads)
    {
        if (x.joinable())
            x.join();
    }

    task_threads.clear();
}

Version ApplicationBase::AppVersion() const
{
    return d->app_version;
}
